/* Copyright Andy Mitchell 2007. No warranties */

/*
	Use the desired Prototype libraries in an Object safe way (i.e. do not add functions to Object/Function/etc.)
	Why? Because we inject into every webpage. We cannot risk upsetting their existing Javascript (the 'for . in . ' issue)
	
*/

var GIPROTOTYPE = {

	decodeEntitiesBox: null,
	decodeEntities: function(s, doc){
		if( !s ) return "";
		if( !GIPROTOTYPE.decodeEntitiesBox ){
			if( !doc ) doc = document;
			GIPROTOTYPE.decodeEntitiesBox = doc.createElement("TEXTAREA")
		}
		GIPROTOTYPE.decodeEntitiesBox.innerHTML = s;
		return GIPROTOTYPE.decodeEntitiesBox.value || "";
	},
	
	searchableLabel: function(labelname){
		if( !labelname ) labelname = "";
		return "label:" + labelname.replace(/ /g, "-");
	},
	
	create: function(elParent, tag, innerHTML, className, options){
		options = options || {};
		var doc = options.doc || document;
		var el = doc.createElement(tag);
		el.innerHTML = innerHTML || "";
		el.className = className || "";
		if( options.style ) el.setAttribute("style", options.style);
		if( elParent ) elParent.appendChild( el );
		return el;
	},
	
	realEmail: function(email){
		// Strip the +...@ format from gmail addresses
		try{
		return email.replace(/\+([^ \.]*?)@/g, "@");
		}catch(e){
			if( GTDDEBUGMODE ) alert("giprototype realEmail\n" + e.toString());
		}
	},
	
	uniqueise: function(arr){
		// Inline remove duplicates in an array
		var L = arr.length;
		for( var i = L-1; i>=0; i-- ){
			for( j = 0; j < L; j++ ){
				if( i!=j && arr[j]==arr[i] ) arr.splice(i,1);
			}
		}
		return arr;
	},
	
	addClassName: function(el, className) {
		if( el.className ){
			el.className += " " + className;
		}else{
			el.className = className;
		}
	},
	removeClassName: function(el, className) {
		if( el.className ){
			el.className = el.className.replace( new RegExp("\\b" + className + "\\b"), "");
			el.className.replace(/\s{2,}/g, " ");
		}
	},
	hasClassName: function(element, className) {
		var elementClassName = element.className;
		if (elementClassName.length == 0) return false;
		if (elementClassName == className || elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) return true;
		return false;
	},
	
	trim: function(s){
		return s.replace(/^[\s\n\r\t]+/, "").replace(/[\s\n\r\t]+$/, "");
	},
	
	prepRE: function(s, options){
		if( options && options.stripSlashes ) s = s.replace(/^\//, "").replace(/\/$/, "");
		return s.replace(/(\"|\'|\\|\^|\$|\*|\+|\?|\=|\!|\:|\||\(|\)|\[|\]|\{|\})/g,"\\$1")
	},
	
	getParent: function(el, options){
		var test = function(elTst){
				if( !el.parentNode ) return true;
				var pass = true;
				if( options.name ) if( elTst.getAttribute("name")!=options.name ) pass = false;
				if( options.className ) if( !GIPROTOTYPE.hasClassName(elTst, options.className) ) pass = false;
				return pass;
			};
		while( !test(el) ){
			el = el.parentNode;
		}
		return el;
	},
	
	animCycle: 25,
	effectQueue: new Object(),
	effectData: new Object(),
	//effectTimer: new Object(),
	
	anim: function(dataUid){
		var dataObj = GIPROTOTYPE.effectData[dataUid];
		if( dataObj.testFunc(dataObj) ){
			clearInterval( dataObj.timer );
			GIPROTOTYPE.effectData[dataObj.uid] = null;
			delete GIPROTOTYPE.effectData[dataObj.uid];
			dataObj.finalFunc(dataObj);
		}
		dataObj = null;
	},
	doEffect: function(el, eName, eData, removeLastInProgress, noQueue){
		// Add this to the queue. If no eName specified; do the next thing for this element.
		
		if( noQueue ){
			GIPROTOTYPE[eName](el, eData, noQueue);	// Jump straight in
			return;
		}
		
		if( !GIPROTOTYPE.effectQueue[el] ) GIPROTOTYPE.effectQueue[el] = [];
		var queue = GIPROTOTYPE.effectQueue[el];
		
		if( eName ){	// Add to queue:
			queue[queue.length] = {"el":el, "eName":eName, "eData":eData};
		}
		
		// Fire next event for this el:
		if( queue.length > 0 ){
			if( removeLastInProgress && queue[0].inProgress ){
				queue.splice(0,1);
			}
			var eReq = queue[0];
			if( !eReq || eReq.inProgress ) return;	// Something else is happening to this element.
			eReq.inProgress = true;
			if( GIPROTOTYPE[eReq.eName] instanceof Function ){
				GIPROTOTYPE[eReq.eName](eReq.el, eReq.eData);
			}
		}	
	},
	genericSlide: function(el, time, elContainer){	
		// Handle Container:
		if( !elContainer ){
			// Create container:
			var dim = GIPROTOTYPE.getDimensions(el);
			elContainer = document.createElement("DIV");
			elContainer.style.width = dim.width + "px";
			elContainer.style.height = dim.height + "px";
			elContainer.setAttribute("created", "1");
			
			// Subsume:
			el.parentNode.insertBefore( elContainer, el);	// Insert same level as el
			elContainer.appendChild( el );	// Add el as child
		}
		if( !elContainer.style.position || elContainer.style.position=="" ){
			elContainer.style.position = "relative";
		}
		elContainer.style.overflow = "hidden";
			
		// Calculate Movement:
		var height = GIPROTOTYPE.getDimensions(el).height;
		var stepsRemaining = time / GIPROTOTYPE.animCycle;
		var pps = (height / stepsRemaining);
		
		
		var dataObj = {"stepsRemaining":stepsRemaining, "pps":pps, "top":height, "height":height, "oldElPos": el.style.position, "oldElTop": el.style.top};
		var testFunc = function(dataObj){
			if( dataObj.stepsRemaining-- <= 0 )	return true;
				dataObj.top += dataObj.pps;
				dataObj.el.style.top =  dataObj.top + "px";
				return false;
			};
		var finalFunc = null;
			
		// Handle El:
		el.style.display = "";
		el.style.position = "absolute";
		
		return [dataObj, testFunc, finalFunc];
	},
	slideInDown: function( el, eData, noQueue ){
		// Slide up from bottom
		// If no elContainer; create one
		// Obtain height of element, that we must move to. Calculate movement based on height and time
		
		try{
			var slideObj = this.genericSlide(el, eData.time, eData.elContainer);
			var dataObj = slideObj[0];
			var testFunc = slideObj[1];
			var finalFunc = slideObj[2];
			var finalFunc = function(dataObj){
				// Destroy container if newly created
				var elContainer = dataObj.el.parentNode;
				if( elContainer.hasAttribute("created") ){
					elContainer.parentNode.insertBefore( dataObj.el, elContainer );
					elContainer.parentNode.removeChild( elContainer );
				}
				dataObj.el.style.position = dataObj.oldElPos;
				dataObj.el.style.top = dataObj.oldElTop;
				if( eData.finalFunc ) eData.finalFunc();
				if( !noQueue ) GIPROTOTYPE.doEffect(dataObj.el, null, null, true);	// Propogate request to more queue items
			};
			
			dataObj.pps *= -1;	// Invert for slide down
									
			// Create default state:
			el.style.top = dataObj.height + "px";
			if( el.style.display=="none" ) el.style.display = "";
						
			// ANIMATE:
			dataObj.el = el;
			dataObj.testFunc = testFunc;
			dataObj.finalFunc = finalFunc;
			//dataObj.finalFunc2 = eData.finalFunc;
			dataObj.uid = parseInt( (new Date()).valueOf() );
			GIPROTOTYPE.effectData[dataObj.uid] = dataObj;
			dataObj.timer = setInterval( function(){GIPROTOTYPE.anim( dataObj.uid )}, GIPROTOTYPE.animCycle);
		}catch(e){
			if( GTDDEBUGMODE ) alert(e.toString());
		}
	},
	slideOutDown: function( el, eData, noQueue ){
		// Slide up from bottom
		// If no elContainer; create one
		// Obtain height of element, that we must move to. Calculate movement based on height and time
		
		try{
			if( el.style.display=="none" || parseInt(el.style.height)<=0 ){
				if( !noQueue ) GIPROTOTYPE.doEffect(el, null, null, true);	// Propogate request to more queue items
				return;
			}
			var slideObj = this.genericSlide(el, eData.time, eData.elContainer);
			var dataObj = slideObj[0];
			var testFunc = slideObj[1];
			var finalFunc = slideObj[2];
			var finalFunc = function(dataObj){
				// Destroy container if newly created
				var elContainer = dataObj.el.parentNode;
				if( elContainer.hasAttribute("created") ){
					elContainer.parentNode.insertBefore( dataObj.el, elContainer );
					elContainer.parentNode.removeChild( elContainer );
				}
				dataObj.el.style.position = dataObj.oldElPos;
				dataObj.el.style.top = dataObj.oldElTop;
				dataObj.el.style.display = "none";
				if( eData.finalFunc ) eData.finalFunc();
				if( !noQueue ) GIPROTOTYPE.doEffect(dataObj.el, null, null, true);	// Propogate request to more queue items
			};
			
			dataObj.top = 0;
			
			// Create default state:
			el.style.top = "0px";
						
			// ANIMATE:
			dataObj.el = el;
			dataObj.testFunc = testFunc;
			dataObj.finalFunc = finalFunc;
			//dataObj.finalFunc2 = eData.finalFunc;
			dataObj.uid = parseInt( (new Date()).valueOf() );
			GIPROTOTYPE.effectData[dataObj.uid] = dataObj;
			dataObj.timer = setInterval( function(){GIPROTOTYPE.anim( dataObj.uid )}, GIPROTOTYPE.animCycle);
		}catch(e){
			if( GTDDEBUGMODE ) alert(e.toString());
		}
	},
	
	disableSelection: function(target){
		if (typeof target.onselectstart!="undefined") //IE route
			target.onselectstart=function(){return false};
		else if (typeof target.style.MozUserSelect!="undefined") //Firefox route
			target.style.MozUserSelect="none";
		else //All other route (ie: Opera)
			target.onmousedown=function(){return false};
		target.style.cursor = "default";
	},
	
	fadeIn: function(el, eData, noQueue ){
		try{
			var fadeCycle = parseInt(GIPROTOTYPE.animCycle * 1.5);
			var dataObj = new Object();
			dataObj.el = el;
			dataObj.opac = 0;
			dataObj.movement =  (1 / (eData.time / fadeCycle));
			dataObj.noQueue = noQueue;
			if( el.style.display!="none" ) dataObj.oldDisplay = el.style.display;
			
			// Initialise:
			dataObj.el.style.opacity = dataObj.opac;
			dataObj.el.style.filter = "alpha(opacity=" + (dataObj.opac * 100) + ")";
			dataObj.el.style.display = "";
			
			if( navigator.userAgent.indexOf( "MSIE" ) > -1 ){	// Claims to be IE, more expensive
				dataObj.testFunc = function(dataObj){
						if( dataObj.opac >= 1 ) return true;
						dataObj.opac = dataObj.opac + dataObj.movement;
						dataObj.el.style.opacity = dataObj.opac;
						dataObj.el.style.filter = "alpha(opacity=" + (dataObj.opac * 100) + ")";
						return false;
					};
			}else{
				dataObj.testFunc = function(dataObj){
						if( dataObj.opac >= 1 ) return true;
						dataObj.opac = dataObj.opac + dataObj.movement;
						dataObj.el.style.opacity = dataObj.opac;
						return false;
					};
			}
			
			dataObj.finalFunc = function(dataObj){
					dataObj.el.style.display = dataObj.oldDisplay || "";
					if( dataObj.finalFunc2 ) dataObj.finalFunc2(dataObj.el);
					if( !noQueue ) GIPROTOTYPE.doEffect(dataObj.el, null, null, true);	// Propogate request to more queue items
				};
			dataObj.finalFunc2 = eData.finalFunc;
			dataObj.uid = parseInt( (new Date()).valueOf() );
			GIPROTOTYPE.effectData[dataObj.uid] = dataObj;
			dataObj.timer = setInterval( function(){GIPROTOTYPE.anim( dataObj.uid )}, fadeCycle);
		}catch(e){
			if( GTDDEBUGMODE ) alert(e.toString());
		}
	},
	fadeOut: function(el, eData, noQueue ){
		try{
			var fadeCycle = parseInt(GIPROTOTYPE.animCycle * 1.5);
			var dataObj = new Object();
			dataObj.el = el;
			dataObj.opac = 1;
			dataObj.movement =  (1 / (eData.time / fadeCycle)) * -1;
			dataObj.noQueue = noQueue;
			
			if( navigator.userAgent.indexOf( "MSIE" ) > -1 ){	// Claims to be IE, more expensive
				dataObj.testFunc = function(dataObj){
						if( dataObj.opac <= 0 ) return true;
						dataObj.opac = dataObj.opac + dataObj.movement;
						dataObj.el.style.opacity = dataObj.opac;
						dataObj.el.style.filter = "alpha(opacity=" + (dataObj.opac * 100) + ")";
						return false;
					};
			}else{
				dataObj.testFunc = function(dataObj){
						if( dataObj.opac <= 0 ) return true;
						dataObj.opac = dataObj.opac + dataObj.movement;
						dataObj.el.style.opacity = dataObj.opac;
						return false;
					};
			}
			
			dataObj.finalFunc = function(dataObj){
					try{
						dataObj.el.style.display = "none";
						dataObj.el.style.opacity = "1";
						dataObj.el.style.filter = "alpha(opacity=100)";
						if( dataObj.finalFunc2 ) dataObj.finalFunc2(dataObj.el);
						if( !noQueue ) GIPROTOTYPE.doEffect(dataObj.el, null, null, true);	// Propogate request to more queue items
					}catch(e){
						if( GTDDEBUGMODE ) alert(e.toString());
					}
				};
			dataObj.finalFunc2 = eData.finalFunc;
			dataObj.uid = parseInt( (new Date()).valueOf() );
			GIPROTOTYPE.effectData[dataObj.uid] = dataObj;
			dataObj.timer = setInterval( function(){GIPROTOTYPE.anim( dataObj.uid )}, fadeCycle);
		}catch(e){
			if( GTDDEBUGMODE ) alert(e.toString());
		}
	},
	
	bindAsEventListener: function(object, method) {
		return function (event) {
			method.call(object, event || window.event);
		};
	},
	bind: function(object, method) {			
		return function () {
			return method.apply(object, arguments);
		};
	},
	
	extend: function(newo, o){
		// Add to newo anything that o has extra
		return GIPROTOTYPE.clone(o, null, newo, true);
	},
	clone: function(o, visited, newo, extending){	// use as clone(object)
		if( !o ) return null;
		if( !visited ) visited = [];
		for( var i = 0; i < visited.length; i++ ) if( visited[i]==o ) return o;
		
		if( o instanceof Array ){
			visited.push(o);
			var a = [];
			for( var i = 0; i < o.length; i++ )	a.push( GIPROTOTYPE.clone(o[i], visited, null, extending) );
			return a;
		}
		var t = (typeof o);
		if( t=="string" || t=="number" || t=="boolean" ) return o;
		
		if( t=="object" && o.hasOwnProperty ){
			visited.push(o);
			newo = newo || {};
			for( p in o ){
				if( o.hasOwnProperty(p) ){
					if( !extending || !newo[p] ) newo[p] = GIPROTOTYPE.clone(o[p], visited, null, extending);	// if extending, only add property if not already known
				}
			}
			return newo;
		}
		
		return o;
	},
	hashKeysToArray: function(h){
		var a = [];
		for( i in h ) a.push(i);
		return a;
	},


	element: function(event){
		return event.target || event.srcElement;
	},
	findParent: function(element, propName, propValue, findHighest){
		// Not in prototype
		// Scan up tree, testing each element[propName]==propValue
		// If not propValue, just find anything with element[propName]
		// If findHighest, scan tree to top
		
		var elMatch = null;
		if( propValue ){
			while( element.parentNode  ){
				if( element[propName] && element[propName]==propValue ){
					elMatch = element;
					if( !findHighest ) break;
				}
				element = element.parentNode;
			}
		}else if( propName ){
			while( element.parentNode  ){
				if( element[propName] ){
					elMatch = element;
					if( !findHighest ) break;
				}
				element = element.parentNode;
			}
		}
		return elMatch;	
	},
	hasParent: function(element, elFind){
		while( element = element.parentNode ){
			if( element==elFind ) return true;
		}
	},
	
	getElementsByClassName: function(el, className){
		var els = el.getElementsByTagName("*");
		var match = [];
		for( var i = 0; i < els.length; i++ ){
			if(GIPROTOTYPE.hasClassName(els[i], className)) match.push(els[i]);
		}
		return match;
	},
	
	doMouseEvent: function(doc, el, eventType, buttonNumber, clientX, clientY){
		buttonNumber = buttonNumber || 0;
		clientX = clientX || 0;
		clientY = clientY || 0;
		
		var clickEvent = doc.createEvent('MouseEvents');
		clickEvent.initMouseEvent(eventType, true, true, doc.defaultView, 1, 0, 0, clientX, clientY, false, false, false, false, buttonNumber, null); 
		el.dispatchEvent( clickEvent );
	},
	
	pointerX: function(event, doc) {
		doc = doc || document;
		return event.pageX || (event.clientX + (doc.documentElement.scrollLeft || doc.body.scrollLeft));
	},

	pointerY: function(event, doc) {
		doc = doc || document;
		return event.pageY || (event.clientY + (doc.documentElement.scrollTop || doc.body.scrollTop));
	},

	stop: function(event) {
		if (event.preventDefault) {
			event.preventDefault();
			event.stopPropagation();
		} else {
			event.returnValue = false;
			event.cancelBubble = true;
		}
	},
	
	observe: function(el, name, f, c){
		c = c || false;
		if( name=="keypress" && (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || el.attachEvent) ) name = "keydown";
		
		if( el.addEventListener ){
			el.addEventListener( name, f, c )
		}else if( el.attachEvent){
			el.attachEvent( "on" + name, f );
		}
	},
	
	stopObserving: function(el, name, f, c){
		c = c || false;
		if( name=="keypress" && (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || el.attachEvent) ) name = "keydown";
		
		if( el.removeEventListener ){
			el.removeEventListener( name, f, c )
		}else if( el.detachEvent){
			el.detachEvent( "on" + name, f );
		}
	},
	
	key: function(event){
		return ((typeof event.which)=="undefined")? event.keyCode : event.which;	// which is more accurate for characters if present (and IE.keyCode=ff.which; IE.keycode!=ff.keycode[scancode], ff.which=ff.charCode [ie does not have charcode])
	},
	keyChar: function(event){
		// Should only be used with keypress (returns different keycodes to keydown/up)
		var kc = GIPROTOTYPE.key(event);
		if( kc==0 ) return "";
		return String.fromCharCode(kc);
	},
	
	addClassName: function(el, cn){
		if( GIPROTOTYPE.hasClassName(el,cn) ) return;
		if( el.className ) cn = el.className + " " + cn;
		el.className = cn;
	},
	removeClassName: function(el, cn){
		el.className = el.className.replace(cn, "");
		el.className = el.className.replace(/\s+$/, "");
	},
	hasClassName: function(el, cn){
		var re = new RegExp("(^|\\s)" + cn + "(\\s|$)");
		return re.test( el.className );
		//return el.className.indexOf(cn) > -1;
	},
	
	scrollTo: function(element) {
		var pos = GIPROTOTYPE.cumulativeOffset(element);
		window.scrollTo(pos[0], pos[1]);
	},
	
	realOffset: function(element) {
		// From viewport?
		var valueT = 0, valueL = 0;
		do {
			valueT += element.scrollTop  || 0;
			valueL += element.scrollLeft || 0;
			element = element.parentNode;
		} while (element);
		return [valueL, valueT];
	},

	cumulativeOffset: function(element) {
		// From top of page
		var valueT = 0, valueL = 0;
		do {
			valueT += element.offsetTop  || 0;
			valueL += element.offsetLeft || 0;
			element = element.offsetParent;
		} while (element);
		return [valueL, valueT];
	},

	positionedOffset: function(element, doc) {
		doc = doc || document;
		var valueT = 0, valueL = 0;
		do {
		valueT += element.offsetTop  || 0;
		valueL += element.offsetLeft || 0;
		element = element.offsetParent;
		if (element) {
			if(element.nodeName.toLowerCase()=='body') break;
			var p = GIPROTOTYPE.getStyle(element, 'position', doc);
			if (p == 'relative' || p == 'absolute') break;
		}
		} while (element);
		return [valueL, valueT];
	},

	pageScrollOffset: function(doc){
		doc = doc || document;
		var deltaX =  window.pageXOffset
					|| doc.documentElement.scrollLeft
					|| doc.body.scrollLeft
					|| 0;
		var deltaY =  window.pageYOffset
					|| doc.documentElement.scrollTop
					|| doc.body.scrollTop
					|| 0;
		return [deltaX, deltaY];
	},

	within: function(element, x, y) {
		var offset = GIPROTOTYPE.cumulativeOffset(element);

		return (y >= offset[1] &&
				y <  offset[1] + element.offsetHeight &&
				x >= offset[0] &&
				x <  offset[0] + element.offsetWidth);
	},
	withinIncludingScrolloffsets: function(element, x, y, doc) {
	doc = doc || document;
   	var deltaX =  window.pageXOffset
                || doc.documentElement.scrollLeft
                || doc.body.scrollLeft
                || 0;
    var deltaY =  window.pageYOffset
                || doc.documentElement.scrollTop
                || doc.body.scrollTop
                || 0;
                
	var offsetcache = GIPROTOTYPE.realOffset(element);
    var xcomp = x + offsetcache[0] - deltaX;
    var ycomp = y + offsetcache[1] - deltaY;
    var offset = GIPROTOTYPE.cumulativeOffset(element);
    
    //alert(offset + "\n" + "\n" + element.offsetWidth + "," + element.offsetHeight + "\n" + xcomp + " & " + ycomp);

    return (ycomp >= offset[1] &&
            ycomp <  offset[1] + element.offsetHeight &&
            xcomp >= offset[0] &&
            xcomp <  offset[0] + element.offsetWidth);
  },
	
	getStyle: function(element, style, doc) {
		doc = doc || document;
		var value = element.style[style];
		if (!value) {
			if (doc.defaultView && doc.defaultView.getComputedStyle) {
				var css = doc.defaultView.getComputedStyle(element, null);
				value = css ? css.getPropertyValue(style) : null;
			} else if (element.currentStyle) {
				value = element.currentStyle[style];
			}
		}
		if (window.opera && GIPROTOTYPE.getStyle(element, 'position', doc) == 'static' ){
			var dimensions = ['left', 'top', 'right', 'bottom'];
			for( var i = 0; i < dimensions.length; i++ ){
				if( style==dimensions[i] ){
					value = "auto";
					break;
				}
			}
		}

		return value == 'auto' ? null : value;
	},
	
	setStyle: function(element, style) {
		for (var name in style) {
		var value = style[name];
		if(name == 'opacity') {
			if (value == 1) {
			value = (/Gecko/.test(navigator.userAgent) &&
				!/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
			if(/MSIE/.test(navigator.userAgent) && !window.opera)
				element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
			} else if(value === '') {
			if(/MSIE/.test(navigator.userAgent) && !window.opera)
				element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
			} else {
			if(value < 0.00001) value = 0;
			if(/MSIE/.test(navigator.userAgent) && !window.opera)
				element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
				'alpha(opacity='+value*100+')';
			}
		} else if(name=="float" || name=="cssFloat") name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
		element.style[GIPROTOTYPE.camelize(name)] = value;
		}
		return element;
	},
	
	camelize: function(str) {
		var parts = str.split('-'),
		len = parts.length;
		if (len == 1) return parts[0];

		var camelized = this.charAt(0) == '-'
		? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
		: parts[0];

		for (var i = 1; i < len; i++)
		camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

		return camelized;
	},
	
	getDimensions: function(element, doc){
		doc = doc || document;
		if (this.getStyle(element, 'display', doc) != 'none')
		return {width: element.offsetWidth, height: element.offsetHeight};

		// All *Width and *Height properties give 0 on elements with display none,
		// so enable the element temporarily
		var els = element.style;
		var originalVisibility = els.visibility;
		var originalPosition = els.position;
		els.visibility = 'hidden';
		els.position = 'absolute';
		els.display = '';
		var originalWidth = element.clientWidth;
		var originalHeight = element.clientHeight;
		els.display = 'none';
		els.position = originalPosition;
		els.visibility = originalVisibility;
		return {width: originalWidth, height: originalHeight};
	},
	
	viewport: function(doc, win, useScrollOffset){
		doc = doc || document;
		var browserWidth = 0;
		var browserHeight = 0;
		if( win.innerWidth ){
			browserWidth = win.innerWidth;
			browserHeight = win.innerHeight;
		}else if( doc.documentElement && doc.documentElement.clientWidth ){
			browserWidth = doc.documentElement.clientWidth;
			browserHeight = doc.documentElement.clientHeight;
		}else{
			browserWidth = doc.body.clientWidth;
			browserHeight = doc.body.clientHeight;
		}

		if( useScrollOffset ){
			var deltaX =  win.pageXOffset
						|| doc.documentElement.scrollLeft
						|| doc.body.scrollLeft
						|| 0;
			var deltaY =  win.pageYOffset
						|| doc.documentElement.scrollTop
						|| doc.body.scrollTop
						|| 0;
			browserWidth -= deltaX;
			browserHeight -= deltaY;
		}
		
		return [browserWidth, browserHeight];
	},
	
	
	getBorderWidth: function(el){
		// Return the width, in pixels, of the border
				
		var borderWidth = GIPROTOTYPE.getStyle( el, "borderWidth" );
		if( !borderWidth ) borderWidth = GIPROTOTYPE.getStyle( el, "border" );
		var borderStyle = GIPROTOTYPE.getStyle( el, "borderStyle" );
		if( borderStyle && borderStyle.indexOf("none")==-1 && borderWidth && (borderWidth = borderWidth.match(/(\d)px/)) ){
			borderWidth = parseInt(borderWidth[1]);
		}else{
			borderWidth = 0;
		}
		
		return borderWidth;
	}
}